package org.example;

import java.util.ArrayList;
import java.util.Objects;

public class QTreeMapAggre {
    static final double lanBase = 100.0;
    static final double latBase = -6.366;
    static final double lanUpTo = 140.0;
    static final double latUpTo = 23.634;
    static final int lanGrids = 4000;
    static final int latGrids = 3000;
    static final int taX = lanGrids / 512 * 512;
    static final int taY = latGrids / 512 * 512;
    /**
     * 我们在测试区域范围(4000, 3000)内切分成了多张512x512的子地图，这是子地图的起点坐标。
     */
    static public class MapBase {
        int mx;
        int my;
        MapBase(int mx, int my) {
            this.mx = mx;
            this.my = my;
        }

        @Override
        public String toString() {
            return "MapBase{" +
                    "mx=" + mx +
                    ", my=" + my +
                    '}';
        }

        @Override
        public boolean equals(Object o) {
            if (o == null || getClass() != o.getClass()) return false;
            MapBase mapBase = (MapBase) o;
            return mx == mapBase.mx && my == mapBase.my;
        }

        @Override
        public int hashCode() {
            return Objects.hash(mx, my);
        }
    }

    static public class Offset {
        int ox;
        int oy;
        Offset(int ox, int oy) {
            this.ox = ox;
            this.oy = oy;
        }

        @Override
        public String toString() {
            return "Offset{" +
                    "ox=" + ox +
                    ", oy=" + oy +
                    '}';
        }
    }

    /**
     * 根据（lan,lat）找对应的地图编号,以及该点在地图内部的坐标偏移量。
     * coordinate
     */
    static public class MapCoordinate {
        double lan;
        double lat;
        MapCoordinate(double lan, double lat) {
            this.lan = lan;
            this.lat = lat;
        }
        MapBase getMapBase() {
            if (lan >= lanUpTo || lan < lanBase || lat >= latUpTo || lat <= latBase) {
                return null;
            }
            int offsetX = (int) ((lan - lanBase)*100);
            int offsetY = (int) ((lat - latBase)*100);
            int xId = (offsetX / 512) * 512;  // 0~511 => 0, 512 ~ 1023 => 512, ... etc
            int yId = (offsetY / 512) * 512;  //
            if (xId > taX) {
                xId = lanGrids - 512;
            }
            if (yId > taY) {
                yId = latGrids - 512;
            }
            return new MapBase(xId, yId);
        }

        Offset getMapOffset() {
            if (lan >= lanUpTo || lan < lanBase || lat >= latUpTo || lat <= latBase) {
                return null;
            }

            int gridX = (int) ((lan - lanBase)*100);
            int gridY = (int) ((lat - latBase)*100);
            int xInSubMap = gridX % 512;
            int yInSubMap = gridY % 512;

            int xId = (gridX / 512) * 512;  // 0~511 => 0, 512 ~ 1023 => 512, ... etc
            int yId = (gridY / 512) * 512;  //
            if (xId > taX) {
                xId = lanGrids - 512;
                xInSubMap = gridX - xId;
            }
            if (yId > taY) {
                yId = latGrids - 512;
                yInSubMap = gridX - yId;
            }
            return new Offset(xInSubMap, yInSubMap);
        }
    }

    /**
     * 一张子图，和它图内的几个点。
     */
    static class PointsInMap {
        MapBase mapBase;
        ArrayList<Offset> points;
        PointsInMap(MapBase mapBase) {
            this.mapBase = mapBase;
            this.points = new ArrayList<>();
        }
        PointsInMap(MapBase mapBase, ArrayList<Offset> points) {
            this.mapBase = mapBase;
            this.points = points;
        }
        void addOffset(Offset offset) {
            this.points.add(offset);
        }

        @Override
        public String toString() {
            return "PointsInMap{" +
                    "mapBase=" + mapBase +
                    ", points=" + points +
                    '}';
        }
    }

    public static void main(String[] args) {
        ArrayList<MapCoordinate> arr = new ArrayList<>();
        arr.add(new MapCoordinate(121.35191085048595, 18.66431561231426));
        arr.add(new MapCoordinate(121.30671673629185, 18.678697160768124));
        arr.add(new MapCoordinate(121.193922127344, 18.62824002651597));
        arr.add(new MapCoordinate(121.17514779693454, 18.634413239338823));
        arr.add(new MapCoordinate(121.10810637554076, 18.652638794471763));
        arr.add(new MapCoordinate(121.01402831740643, 18.626123884329893));
        arr.add(new MapCoordinate(120.85686168606969, 18.646845605566536));
        arr.add(new MapCoordinate(120.81072315243217, 18.654254927525486));
        arr.add(new MapCoordinate(120.77055426167718, 18.627194972068807));
        arr.add(new MapCoordinate(120.68913491795365, 18.587583536534382));
        arr.add(new MapCoordinate(120.60771557423014, 18.54797210099996));
        arr.add(new MapCoordinate(120.5630822237347, 18.508840434301614));
        arr.add(new MapCoordinate(120.33634197190321, 18.431473420489027));
        arr.add(new MapCoordinate(120.34230880535893, 18.378966036840943));
        arr.add(new MapCoordinate(120.33952420903589, 18.340999361691054));
        arr.add(new MapCoordinate(120.2849459385072, 18.22361848302763));
        arr.add(new MapCoordinate(120.26271967785, 17.990816283582667));
        arr.add(new MapCoordinate(120.22795643256258, 17.757857143323875));
        arr.add(new MapCoordinate(120.32395642394123, 17.557953704516116));
        arr.add(new MapCoordinate(120.36133764461127, 17.441895522986897));
        arr.add(new MapCoordinate(120.2753383196744, 17.2491111163098));
        arr.add(new MapCoordinate(120.2750687935569, 17.032358879030703));
        arr.add(new MapCoordinate(120.31146276854444, 16.923457130252533));
        arr.add(new MapCoordinate(120.31024461549156, 16.82287881351573));
        arr.add(new MapCoordinate(120.30220748775554, 16.77844630078236));
        arr.add(new MapCoordinate(120.28946427465016, 16.73357269686565));
        arr.add(new MapCoordinate(120.27672106154476, 16.688699092948944));
        arr.add(new MapCoordinate(120.26613368969967, 16.611908227959837));
        arr.add(new MapCoordinate(120.27763048360082, 16.552384063430424));
        arr.add(new MapCoordinate(120.28912727750196, 16.49285989890101));
        arr.add(new MapCoordinate(120.32683650241731, 16.288874058155898));
        arr.add(new MapCoordinate(120.26721214725859, 16.165784142216992));
        arr.add(new MapCoordinate(120.29891766050167, 16.118272376829893));


        ArrayList<PointsInMap> t = new ArrayList<>();

        arr.forEach(mapCoordinate -> {
            MapBase mapBase = mapCoordinate.getMapBase();
            boolean exists = false;
            PointsInMap existsElement = null;
            for (var it : t) {
                if (it.mapBase.equals(mapBase)) {
                    exists = true;
                    existsElement = it;
                    break;
                }
            }
            if (!exists) {
                PointsInMap newObj = new PointsInMap(mapBase);
                newObj.addOffset(mapCoordinate.getMapOffset());
                t.add(newObj);
            } else {
                existsElement.addOffset(mapCoordinate.getMapOffset());
            }
        });

        t.forEach(System.out::println);
    }
}